#include "../include/LHG_Crypt.h"

void lhg_genkeyRSA(const char *PubPath, const char *PrivPath, unsigned int bits)
{
    int ret = 0;
    RSA *r = NULL;
    BIGNUM *bne = NULL;
    BIO *bp_public = NULL, *bp_private = NULL;
    unsigned long e = RSA_F4;
    bne = BN_new();
    ret = BN_set_word(bne, e);
    r = RSA_new();
    ret = RSA_generate_key_ex(r, bits, bne, NULL);
    bp_public = BIO_new_file(PubPath, "w+");
    ret = PEM_write_bio_RSAPublicKey(bp_public, r);
    bp_private = BIO_new_file(PrivPath, "w+");
    ret = PEM_write_bio_RSAPrivateKey(bp_private, r, NULL, NULL, 0, NULL, NULL);
    BIO_free_all(bp_public);
    BIO_free_all(bp_private);
    RSA_free(r);
    BN_free(bne);
}

void lhg_encrypt(const char *RSAPubPath, const char *In, const char *Out)
{
    AES_KEY AESkey;
    int size;
    unsigned char key[16];
    unsigned char iv[16];
    unsigned char indata[AES_BLOCK_SIZE];
    unsigned char outdata[AES_BLOCK_SIZE];
    int postion = 0;
    int bytes_read, bytes_write;
    RSA *rsa = NULL;
    FILE *f = NULL;
    FILE *in = NULL;
    FILE *out = NULL;

    RAND_bytes(key, sizeof(key));
    RAND_bytes(iv, sizeof(iv));
    AES_set_encrypt_key(key, 128, &AESkey);

    f = fopen(RSAPubPath, "rb");
    PEM_read_RSAPublicKey(f, &rsa, NULL, NULL);
    fclose(f);
    f = NULL;

    size = RSA_size(rsa);
    unsigned char encrypt[size];

    RSA_public_encrypt(sizeof(key), key, encrypt, rsa, RSA_PKCS1_PADDING);
    out = fopen(Out, "wb+");
    bytes_write = fwrite(encrypt, 1, sizeof(encrypt), out);
    RSA_public_encrypt(sizeof(iv), iv, encrypt, rsa, RSA_PKCS1_PADDING);
    bytes_write = fwrite(encrypt, 1, sizeof(encrypt), out);
    in = fopen(In, "rb");

    RSA_free(rsa);

    while (1)
    {
        unsigned char ivec[AES_BLOCK_SIZE];
        memcpy(ivec, iv, AES_BLOCK_SIZE);
        bytes_read = fread(indata, 1, AES_BLOCK_SIZE, in);
        AES_cfb128_encrypt(indata, outdata, bytes_read, &AESkey, ivec, &postion, AES_ENCRYPT);
        bytes_write = fwrite(outdata, 1, bytes_read, out);
        if (bytes_read < AES_BLOCK_SIZE)
            break;
    }

    fclose(in);
    in = NULL;
    fclose(out);
    out = NULL;
}

void lhg_decrypt(const char *RSAPrivPath, const char *In, const char *Out)
{
    AES_KEY AESkey;
    int size;
    unsigned char key[16];
    unsigned char iv[16];
    unsigned char indata[AES_BLOCK_SIZE];
    unsigned char outdata[AES_BLOCK_SIZE];
    int postion = 0;
    int bytes_read, bytes_write;
    RSA *rsa = NULL;
    FILE *f = NULL;
    FILE *in = NULL;
    FILE *out = NULL;

    f = fopen(RSAPrivPath, "rb");
    PEM_read_RSAPrivateKey(f, &rsa, NULL, NULL);
    fclose(f);
    f = NULL;

    size = RSA_size(rsa);
    unsigned char decrypt[size];

    in = fopen(In, "rb");
    bytes_read = fread(decrypt, 1, sizeof(decrypt), in);
    RSA_private_decrypt(sizeof(decrypt), decrypt, key, rsa, RSA_PKCS1_PADDING);
    bytes_read = fread(decrypt, 1, sizeof(decrypt), in);
    RSA_private_decrypt(sizeof(decrypt), decrypt, iv, rsa, RSA_PKCS1_PADDING);

    RSA_free(rsa);

    AES_set_encrypt_key(key, 128, &AESkey);

    out = fopen(Out, "wb+");

    while (1)
    {
        unsigned char ivec[AES_BLOCK_SIZE];
        memcpy(ivec, iv, AES_BLOCK_SIZE);
        bytes_read = fread(indata, 1, AES_BLOCK_SIZE, in);
        AES_cfb128_encrypt(indata, outdata, bytes_read, &AESkey, ivec, &postion, AES_DECRYPT);
        bytes_write = fwrite(outdata, 1, bytes_read, out);
        if (bytes_read < AES_BLOCK_SIZE)
            break;
    }

    fclose(in);
    in = NULL;
    fclose(out);
    out = NULL;
}
